/****************************************************************
 *  This file created by writerB.php on 03/20/2007 at 20:22:18  *
 ****************************************************************
 *  writerB.php created by Tonk (tonk@ctonk.com) in 2007        *
 ****************************************************************
 *                 General Document Information
 *
 *  File Name: area.cpp
 *  Class Name: Area
 *  Data members: 9
 *  Notes:
 ****************************************************************/

#include <cstring>
#include <fstream>
#include <iostream>
#include "combined.h"
#include "definitions.h"
#include "log.h"
#include "stringutil.h"
#include "world.h"



int Area::numAreas = 0;
int Area::areaIndex = 0;


// Default constructor...
Area::Area() {
	setAreaID( ++areaIndex );
	setFirstv( 0 );
	setLastv( 0 );
	setSize( 0 );
	setFlags( 0 );
	// Increment the object count...
	numAreas++;
}

// Copy constructor (Doesn't change _AreaID)...
Area::Area( const Area& ref ) {
	setAreaID( ++areaIndex );
	setFirstv( ref.getFirstv() );
	setLastv( ref.getLastv() );
	setSize( ref.getSize() );
	setFlags( ref.getFlags() );
	setNextReset( ref.getNextReset() );
	setKeyword( ref.getKeyword() );
	setName( ref.getName() );
	// Increment the object count...
	numAreas++;
}

// Additional constructors...
Area::Area( const std::string& keyword ) {
	setAreaID( ++areaIndex );
	setKeyword( keyword );
	setFirstv( 0 );
	setLastv( 0 );
	setSize( 0 );
	setFlags( 0 );
	// Increment the object count...
	numAreas++;
}

// Destructor...
Area::~Area() {
	for ( std::vector< Room* >::iterator it = getRooms().begin(); it != getRooms().end(); ) {
		delete (*it);
		it = getRooms().erase( it );
	}
	// Decrement the object count...
	numAreas--;
}

// Assignment operator (Doesn't change _AreaID)...
Area& Area::operator = ( const Area& ref ) {
	setFirstv( ref.getFirstv() );
	setLastv( ref.getLastv() );
	setSize( ref.getSize() );
	setFlags( ref.getFlags() );
	setNextReset( ref.getNextReset() );
	setKeyword( ref.getKeyword() );
	setName( ref.getName() );
	return *this;
}

// Equality operator (Compares _firstv)...
bool Area::operator == ( const Area& ref ) const {
	return getFirstv() == ref.getFirstv();
}

// Inequality operator (Compares _firstv)...
bool Area::operator != ( const Area& ref ) const {
	return getFirstv() != ref.getFirstv();
}

// Less than operator (Compares _firstv)...
bool Area::operator < ( const Area& ref ) const {
	return getFirstv() < ref.getFirstv();
}

// Greater than operator (Compares _firstv)...
bool Area::operator > ( const Area& ref ) const {
	return getFirstv() > ref.getFirstv();
}

// Protected Methods...
void Area::setAreaID( const int& AreaID ) { // protected
	_AreaID = AreaID;
}

// Public accessor methods...
int Area::getAreaID( void ) const { // public
	return _AreaID;
}

void Area::setFirstv( const unsigned& firstv ) { // public
	_firstv = firstv;
	return;
}

unsigned Area::getFirstv( void ) const { // public
	return _firstv;
}

void Area::setLastv( const unsigned& lastv ) { // public
	_lastv = lastv;
	return;
}

unsigned Area::getLastv( void ) const { // public
	return _lastv;
}

void Area::setSize( const unsigned& size ) { // public
	_size = size;
	return;
}

unsigned Area::getSize( void ) const { // public
	return _size;
}

void Area::setFlags( const int& flags ) { // public
	_flags = flags;
	return;
}

int Area::getFlags( void ) const { // public
	return _flags;
}

void Area::setNextReset( const time_t& nextReset ) { // public
	_nextReset = nextReset;
	return;
}

time_t Area::getNextReset( void ) const { // public
	return _nextReset;
}

void Area::setKeyword( const std::string& keyword ) { // public
	_keyword = keyword;
	_file = AREA_DIR + getKeyword() + ".area";
	return;
}

std::string Area::getKeyword( void ) const { // public
	return _keyword;
}

std::string Area::getFile( void ) const { // public
	return _file;
}

void Area::setName( const std::string& name ) { // public
	_name = name;
	return;
}

std::string Area::getName( void ) const { // public
	return _name;
}

std::vector< Room* >& Area::getRooms( void ) { // public
	return _rooms;
}

std::vector< Mob* >& Area::getMobs( void ) { // public
	return _mobs;
}

std::vector< Object* >& Area::getObjects( void ) { // public
	return _objects;
}

std::vector< Reset* >& Area::getResets( void ) { // public
	return _resets;
}

std::vector< std::string >& Area::getPermissions( void ) { // public
	return _permissions;
}

// General methods...
Room* Area::addRoom( void ) { // public
	return new Room( 0, this );
}

Room* Area::add( Room* room ) { // public
	getRooms().push_back( room );
	return room;
}

Room* Area::getRoom( const unsigned& vnum ) { // public
	unsigned u = 0;
	for ( std::vector< Room* >::iterator it = getRooms().begin(); it != getRooms().end(); ++it, ++u )
		if ( u == vnum )
			return *it;
	return NULL;
}

bool Area::drop( Room* room ) { // public
	for ( std::vector< Room* >::iterator it = getRooms().begin(); it != getRooms().end(); ++it )
		if ( (*it) == room ) {
			getRooms().erase( it );
			return true;
		}
	return false;
}

Mob* Area::addMob( void ) { // public
	return add( new Mob );
}

Mob* Area::add( Mob* mob ) { // public
	getMobs().push_back( mob );
	return mob;
}

Mob* Area::getMob( const unsigned& vnum ) { // public
	unsigned u = 0;
	for ( std::vector< Mob* >::iterator it = getMobs().begin(); it != getMobs().end(); ++it, ++u )
		if ( u == vnum )
			return *it;
	return NULL;
}

bool Area::drop( Mob* mob ) { // public
	for ( std::vector< Mob* >::iterator it = getMobs().begin(); it != getMobs().end(); ++it )
		if ( (*it) == mob ) {
			getMobs().erase( it );
			return true;
		}
	return false;
}

void Area::readObject( std::istream& read ) { // public
	int vnum = 0, type = 0, flags = 0;
	char temp[1024];
	std::string name, shortDesc, longDesc, fullDesc;

	read >> vnum >> type >> flags;
	read.get();	// This kills an extra newline that we don't want.
	read.getline( temp, 1024 );
	name = temp;
	read.getline( temp, 1024 );
	shortDesc = temp;
	read.getline( temp, 1024 );
	longDesc = temp;
	read.getline( temp, 1024 );
	fullDesc = temp;

	Object* object = Object::getNewObject( (OBJECT_TYPE)type );

	object->setVnum( vnum );
	object->setType( (OBJECT_TYPE)type );
	object->setFlags( flags );
	object->setName( name );
	object->setShortDesc( shortDesc );
	object->setLongDesc( longDesc );
	object->setFullDesc( fullDesc );

	object->load( read );

	return;
}

Object* Area::add( Object* object ) { // public
	getObjects().push_back( object );
	return object;
}

Object* Area::getObject( const unsigned& vnum ) { // public
	unsigned u = 0;
	for ( std::vector< Object* >::iterator it = getObjects().begin(); it != getObjects().end(); ++it, ++u )
		if ( u == vnum )
			return *it;
	return NULL;
}

bool Area::drop( Object* object ) { // public
	for ( std::vector< Object* >::iterator it = getObjects().begin(); it != getObjects().end(); ++it )
		if ( (*it) == object ) {
			getObjects().erase( it );
			return true;
		}
	return false;
}

Object* Area::createObject( const unsigned& vnum, const unsigned& room_vnum, const unsigned& number, const unsigned& limit ) { // public
	std::string message;
	Object* object = NULL;
	Room* room = NULL;
	Area* area = NULL;

	// Find the correct Room...
	if ( ( room = World::Instance().getRoom( room_vnum ) ) == NULL ) {
		message << "getRoom (called from Area::createObject) returned NULL in " << __FILE__ << " line " << __LINE__;
		log( message, WORLD );
		return NULL;
	}
	// Find the correct Area...
	if ( ( area = room->getArea() ) == NULL ) {
		message << "Room::getArea (called from Area::createObject) returned NULL in " << __FILE__ << " line " << __LINE__;
		log( message, WORLD );
		return NULL;
	}

	for ( std::vector< Object* >::iterator it = area->getObjects().begin(); it != area->getObjects().end(); it++ )
		if ( (*it)->getVnum() == vnum )
				object = Object::getNewObject( (*it)->getType(), *it );

	if ( object == NULL ) // object doesn't exist
		return NULL;

	// Object was found and created successfully, let's integrate it.
	World::Instance().add( object );
	room->add( object );

	return object;
}

Reset* Area::addReset( void ) { // public
	return add( new Reset );
}

Reset* Area::add( Reset* reset ) { // public
	getResets().push_back( reset );
	return reset;
}

Reset* Area::getReset( const int& i ) { // public
	int j=0;
	for ( std::vector< Reset* >::iterator it = getResets().begin(); it != getResets().end(); it++, j++ )
		if ( j == i )
			return *it;
	return NULL;
}

bool Area::dropReset( const int& i ) { // public
	int j=0;
	for ( std::vector< Reset* >::iterator it = getResets().begin(); it != getResets().end(); it++, j++ )
		if ( j == i ) {
			getResets().erase( it );
			return true;
		}
	return false;
}

void Area::addPermission( const std::string& name ) { // public
	getPermissions().push_back( name );
	return;
}

bool Area::hasPermission( Character* character ) { // public
	if ( getKeyword() == "symphony" )
		return false;
	if ( character->getLevel() >= GOD )
		return true;
	for ( std::vector< std::string >::iterator it = getPermissions().begin(); it != getPermissions().end(); it++ )
		if ( (*it) == character->getName() )
			return true;
	return false;
}

bool Area::dropPermission( const std::string& name) { // public
	for ( std::vector< std::string >::iterator it = getPermissions().begin(); it != getPermissions().end(); it++ )
		if ( (*it) == name ) {
			getPermissions().erase( it );
			return true;
		}
	return false;
}

void Area::reset( void ) { // public
	time_t current;
	std::string message;
	Reset* reset = NULL;
	Room* room = NULL;

	for ( std::vector< Reset* >::iterator it = getResets().begin(); it != getResets().end(); it++ ) {
		reset = *it;
		////////////////////////////////// Mob Resets...
		if ( reset->getType() == MOB ) {
			Mob* target = NULL;
			Mob* mob = NULL;
			unsigned mobCount = 0;
			unsigned added = 0;
			for ( std::map<std::string,Mob*>::iterator it = World::Instance().getMobs().begin(); it != World::Instance().getMobs().end(); it++ )
				if ( it->second->getArea() == this && it->second->getVnum() == (unsigned)reset->getTarget() )
					mobCount++;
			for ( std::vector< Mob* >::iterator it = getMobs().begin(); it != getMobs().end(); it++ )
				if ( (*it)->getVnum() == reset->getTarget() )
					target = *it;
			if ( target != NULL ) {
				while ( mobCount < reset->getLimit() && added < reset->getNumber() ) {
					mob = new Mob( *target );
					World::Instance().add( mob );
					if ( ( room = World::Instance().getRoom( reset->getVnum() ) ) == NULL ) {
						message.clear();
						message << "World::getRoom() returned NULL with vnum = " << reset->getVnum();
						log( message, WORLD );
						continue;
					}
					room->add( mob );
					mob->setRoom( room );
					mobCount++;
					added++;
				}
			} else
				message.clear();
				message << "In Area::resetArea() Mob " << reset->getTarget() << " didn't load into room " << reset->getVnum();
				log( message, WORLD );
		}
		////////////////////////////////// Object Resets...
		if ( reset->getType() == OBJECT ) {
			Object* target = NULL;
			unsigned objCount = 0;
			for ( std::multimap<std::string,Object*>::iterator it = World::Instance().getObjects().begin(); it != World::Instance().getObjects().end(); it++ )
				if ( it->second->getVnum() == reset->getTarget() )
					objCount++;
			for ( std::vector< Object* >::iterator it = getObjects().begin(); it != getObjects().end(); it++ )
				if ( (*it)->getVnum() == reset->getTarget() )
					target = *it;
			if ( target != NULL )
				createObject( target->getVnum(), reset->getVnum(), reset->getNumber(), reset->getLimit() );
			else
				message.clear();
				message << "In Area::resetArea() Object " << reset->getTarget() << " didn't load into room " << reset->getVnum();
				log( message, WORLD );
		}
	}

	// Set the time for the next reset...
	time( &current );
	setNextReset( current + (time_t)Timestamp::Instance().random( LOWER_RESET, UPPER_RESET ) );

	return;
}

// Static methods...

// Associated operators...
std::ostream& operator << ( std::ostream& write, Area& area ) { // associated
	// Write general area info...
	write << area.getFirstv() << ' ' << area.getLastv() << ' ' << area.getKeyword() << '\n';
	write << area.getName() << '\n';
	write << area.getPermissions().size() << ' ';
	for ( std::vector< std::string >::iterator it = area.getPermissions().begin(); it != area.getPermissions().end(); it++ )
		write << (*it) << ' ';
	write << '\n';

	// Save rooms in sequence...
	for ( unsigned u = area.getFirstv(); u <= area.getLastv(); u++ )
		for ( std::vector< Room* >::iterator it = area.getRooms().begin(); it != area.getRooms().end(); it++ )
			if ( (*it)->getVnum() == u )
				write << (*it);

	// Save mobs in sequence...
	for ( unsigned u = area.getFirstv(); u <= area.getLastv(); u++ )
		for ( std::vector< Mob* >::iterator it = area.getMobs().begin(); it != area.getMobs().end(); it++ )
			if ( (*it)->getVnum() == u )
				write << (*it);

	// Save objects in sequence...
	for ( unsigned u = area.getFirstv(); u <= area.getLastv(); u++ )
		for ( std::vector< Object* >::iterator it = area.getObjects().begin(); it != area.getObjects().end(); it++ )
			if ( (*it)->getVnum() == u )
				write << (*it);

	// Save resets in sequence...
	for ( unsigned u = area.getFirstv(); u <= area.getLastv(); u++ )
		for ( std::vector< Reset* >::iterator it = area.getResets().begin(); it != area.getResets().end(); it++ )
			if ( (*it)->getVnum() == u )
				write << (*it);

	return write;
}

std::ostream& operator << ( std::ostream& write, Area* area ) { // associated
	write << *area;
	return write;
}

std::istream& operator >> ( std::istream& read, Area& area ) { // associated
	unsigned firstv = 0, lastv = 0, numberPerms = 0;
	std::string keyword, perm;
	char temp[2048];

	// Read in general area data...
	read >> firstv >> lastv >> keyword;
	area.setFirstv( firstv );
	area.setLastv( lastv );
	area.setSize( lastv - firstv + 1 );
	area.setKeyword( keyword );

	read.get();
	read.getline( temp, 2048 );
	area.setName( temp );

	read >> numberPerms;
	for ( unsigned u=0; u < numberPerms; u++ ) {
		read >> perm;
		area.addPermission( perm );
	}


	// Read in Rooms, Mobs, Objects, Resets, etc...
	while ( read.peek() != EOF ) {
		read.getline( temp, 2048 );
		if ( strcmp( temp, "**ROOM" ) == 0 )
			read >> area.add( new Room( 0, &area ) );
		else if ( strcmp( temp, "**MOB" ) == 0 )
			read >> area.addMob();
		else if ( strcmp( temp, "**OBJECT" ) == 0 )
			// We have to load objects through a special method because the base
			// object class is an ADT, and we want to load all the base object
			// data and then decide which subtype of object to create.
			area.readObject( read );
		else if ( strcmp( temp, "**RESET" ) == 0 )
			read >> area.addReset();
	}

	area.reset();

	return read;
}

std::istream& operator >> ( std::istream& read, Area* area ) { // associated
	read >> *area;
	return read;
}
